package com.tbr.blockchain.p2p;

import com.alibaba.fastjson.JSON;
import com.tbr.blockchain.block.BlockService;
import com.tbr.blockchain.model.Block;
import com.tbr.blockchain.model.Transaction;
import com.tbr.blockchain.model.Wallet;
import org.java_websocket.WebSocket;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

@Service
@Component("p2pService")
public class P2PService {
    private List<WebSocket> sockets;
    @Resource
    private BlockService blockService;


//    查询最新区块
    public final static int QUERY_LATEST_BLOCK=0;
//    查询整个区块链
    public final static int QUERY_BLOCKCHAIN=1;
//    查询交易
    public final static int QUERY_TRANSACTION=2;
//    查询打包的交易
    public final static int QUERY_PACKED_TRANSACTION=3;
//    返回钱包
    public final static int QUERY_WALLET=4;
//    返回区块链
    public final static int RESPONSE_BLOCKCHAIN=5;
//    返回交易
    public final static int RESPONSE_TRANSACTION=6;
//    返回打包的交易
    public final static int RESPONSE_PACKED_TRANSACTION=7;
//    返回钱包
    public final static int RESPONSE_WALLET=8;

    public  P2PService(BlockService blockService){
        this.blockService=blockService;
        this.sockets=new ArrayList<WebSocket>();
    }

    public List<WebSocket> getSockets(){
        return sockets;
    }

    public synchronized void handleMessage(WebSocket webSocket,String msg,List<WebSocket> sockets){
        try {
            Message message= JSON.parseObject(msg, Message.class);
            System.out.println("接收到"+webSocket.getRemoteSocketAddress().getPort()+"的p2p消息"+ JSON.toJSONString(message));
            switch (message.getType()){
                case QUERY_LATEST_BLOCK:
                    write(webSocket,responseLatestBlockMsg());
                    break;
                case QUERY_BLOCKCHAIN:
                    write(webSocket,responseBlockChainMsg());
                    break;
                case QUERY_TRANSACTION:
                    write(webSocket,responseTransactions());
                    break;
                case QUERY_PACKED_TRANSACTION:
                    write(webSocket,responsePackedTransactions());
                    break;
                case QUERY_WALLET:
                    write(webSocket,responseWallet());
                    break;
                case RESPONSE_BLOCKCHAIN:
                    handleBlockChainResponse(message.getData(),sockets);
                    break;
                case RESPONSE_TRANSACTION:
                    handleTransactionResponse(message.getData());
                    break;
                case RESPONSE_PACKED_TRANSACTION:
                    handlePackedTransactionResponse(message.getData());
                    break;
                case RESPONSE_WALLET:
                    handleWalletResponse(message.getData());
                    break;
            }
        }catch (Exception e){
            System.out.println("处理p2p消息错误:"+e.getMessage());
        }
    }


    public String queryBlockChainMsg(){
        return JSON.toJSONString(new Message(QUERY_BLOCKCHAIN));
    }

    public String queryLatestBlockMsg(){
        return JSON.toJSONString(new Message(QUERY_LATEST_BLOCK));
    }

    public String queryTransactionMsg(){
        return JSON.toJSONString(new Message(QUERY_TRANSACTION));
    }

    public String queryPackedTransactionMsg(){
        return JSON.toJSONString(new Message(QUERY_PACKED_TRANSACTION));
    }

    public String queryWalletMsg(){
        return JSON.toJSONString(new Message(QUERY_WALLET));
    }

    public String responseBlockChainMsg(){
        return JSON.toJSONString(new Message(RESPONSE_BLOCKCHAIN,JSON.toJSONString(blockService.getBlocksChain())));
    }

    public String responseLatestBlockMsg(){
        Block[]  blocks={blockService.getLatestBlock()};
        return JSON.toJSONString(new Message(RESPONSE_BLOCKCHAIN,JSON.toJSONString(blocks)));
    }

    public String responseTransactions(){
        return JSON.toJSONString(new Message(RESPONSE_TRANSACTION,JSON.toJSONString(blockService.getAllTransaction())));
    }

    public String responsePackedTransactions(){
        return JSON.toJSONString(new Message(RESPONSE_PACKED_TRANSACTION,JSON.toJSONString(blockService.getPackedTransaction())));
    }

    public String responseWallet(){
        List<Wallet> wallets=new ArrayList<Wallet>();
        blockService.getMyWalletMap().forEach((address,wallet) -> {
            wallets.add(new Wallet(wallet.getPublicKey()));
        });
        blockService.getOtherWalletMap().forEach((address,wallet) ->{
            wallets.add(wallet);
        });
        return JSON.toJSONString(new Message(RESPONSE_WALLET,JSON.toJSONString(wallets)));
    }

    public void handleBlockChainResponse(String message,List<WebSocket> sockets){
        List<Block> receiveBlockchain=JSON.parseArray(message,Block.class);
        Collections.sort(receiveBlockchain, new Comparator<Block>() {
            @Override
            public int compare(Block o1, Block o2) {
                return o1.getIndex()-o2.getIndex();
            }
        });
        Block latestBlockReceived=receiveBlockchain.get(receiveBlockchain.size()-1);
        Block latestBlock=blockService.getLatestBlock();
        if(latestBlockReceived.getIndex()>latestBlock.getIndex()){
            if(latestBlock.getHash().equals(latestBlockReceived.getPreviousHash())){
                System.out.println("将新加入的区块加入本地区块链");
                if(blockService.addBlock(latestBlockReceived)){
                    List<Transaction> txs = latestBlockReceived.getTransactions();
                    Transaction reward=new Transaction();
                    for(Transaction tx:txs){
                        if(tx.coinBaseTx()){
                            reward=tx;
                            break;}
                    }
                    if(reward!=null){
                        if(!blockService.getAllTransaction().contains(reward))
                            blockService.addTransaction(reward);
                    }
                    broatcast(responseLatestBlockMsg());
                }
            }
            else if(receiveBlockchain.size()==1){
                System.out.println("查看所有通讯节点上的区块链");
                broatcast(queryBlockChainMsg());
            }
            else{
                blockService.replaceChain(receiveBlockchain);
            }
        }else{
            System.out.println("接受的区块链不比本地区块区块链长，不处理");
        }
    }


    public void handleWalletResponse(String msg){
        List<Wallet> wallets=JSON.parseArray(msg,Wallet.class);
        wallets.forEach(wallet -> {
            blockService.getOtherWalletMap().put(wallet.getAddress(),wallet);
        });
    }

    public void handleTransactionResponse(String msg){
        List<Transaction> txs=JSON.parseArray(msg,Transaction.class);
        blockService.getAllTransaction().addAll(txs);
    }

    public void handlePackedTransactionResponse(String msg){
        List<Transaction> txs=JSON.parseArray(msg,Transaction.class);
        blockService.getPackedTransaction().addAll(txs);

    }

    /**
     * 向客户端发送消息
     * @param ws
     * @param message
     */
    public void write(WebSocket ws, String message) {
        System.out.println("发送给" + ws.getRemoteSocketAddress().getPort() + "的p2p消息:" + message);
        ws.send(message);
    }
    /**
     * 向所有客户端广播消息
     * @param message
     */
    public void broatcast(String message) {
        if (sockets.size() == 0) {
            return;
        }
        System.out.println("======广播消息开始：");
        for (WebSocket socket : sockets) {
            this.write(socket, message);
        }
        System.out.println("======广播消息结束");
    }



}
